"""
JWT 令牌服务
"""
import uuid
from datetime import datetime, timedelta, timezone
from typing import Dict, Any
import logging

import jwt
from jwt import PyJWTError

from axiom_boot.conf.manager import Settings
from axiom_boot.core.exceptions import Unauthorized
from axiom_boot.di import autowired, service, conditional_on_setting
from axiom_boot.cache.service import CacheService

class CredentialsException(Unauthorized):
    def __init__(self, message: str = "无法验证凭据", status_code: int = 401):
        super().__init__(message=message)
        # 为异常实例附加 status_code，供中间件的异常处理器捕获
        self.status_code = status_code


logger = logging.getLogger(__name__)


@service()
@conditional_on_setting(key="oauth_enabled")
class TokenService:
    """封装JWT令牌的生成和验证，并包含 JTI 黑名单机制用于令牌吊销。"""

    def __init__(self, settings: Settings = autowired(), cache_service: CacheService = autowired()):
        self._secret_key = settings.oauth_secret_key
        self._algorithm = settings.oauth_algorithm
        self._expire_minutes = settings.oauth_access_token_expire_minutes
        self._refresh_expire_days = 30  # Refresh token 有效期，可以配置
        self._cache_service = cache_service

    def create_access_token(
        self, claims: Dict[str, Any]
    ) -> str:
        """创建一个新的访问令牌，并包含一个唯一的 JTI"""
        to_encode = claims.copy()
        
        expire = datetime.now(timezone.utc) + timedelta(minutes=self._expire_minutes)
        to_encode.update({
            "exp": expire,
            "iat": datetime.now(timezone.utc), # iat: 签发时间
            "jti": str(uuid.uuid4()) # jti: JWT ID (令牌唯一标识), 用于吊销
        })
        return jwt.encode(to_encode, self._secret_key, algorithm=self._algorithm)

    def create_refresh_token(self, claims: Dict[str, Any]) -> str:
        """创建一个新的刷新令牌"""
        expire = datetime.now(timezone.utc) + timedelta(days=self._refresh_expire_days)
        claims_to_encode = claims.copy()
        claims_to_encode.update({"exp": expire, "jti": str(uuid.uuid4())}) # 刷新令牌同样需要 JTI
        return jwt.encode(claims_to_encode, self._secret_key, algorithm=self._algorithm)

    async def decode_token(self, token: str) -> Dict[str, Any]:
        """解码并验证令牌，同时检查 JTI 黑名单，返回载荷"""
        try:
            payload = jwt.decode(token, self._secret_key, algorithms=[self._algorithm])
            
            jti = payload.get("jti")
            if not jti:
                raise CredentialsException(message="令牌缺少 JTI，无法验证有效性")

            # 检查 JTI 是否在吊销黑名单中
            if await self._cache_service.get(f"jti_blacklist:{jti}"):
                raise CredentialsException(message="令牌已被吊销")
                
            return payload
        except PyJWTError as e:
            raise CredentialsException(message=f"令牌无效或已过期: {e}")
